<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, shallowRef, toRaw, type PropType } from 'vue'
import Uploader from 'simple-uploader.js'
import type { CustomFile } from './types'
import { PauseRound, PlayArrowRound, AutorenewRound, DeleteOutlineRound } from '@vicons/material'
import { NIcon } from 'naive-ui'

const props = defineProps({
  file: {
    type: Object as PropType<CustomFile>,
    required: true
  }
})

const events = ['fileProgress', 'fileSuccess', 'fileComplete', 'fileError']
const response = shallowRef<any>(null)
const progress = ref(0)
const averageSpeed = ref(0)
const currentSpeed = ref(0)
const timeRemaining = ref(0)
const uploadedSize = ref(0)
const paused = ref(false)
const error = ref(false)
const isUploading = ref(false)
const isComplete = ref(false)

const handlers = {} as Record<string, Function>
onMounted(() => {
  events.forEach((event) => {
    props.file.uploader.on(event, fileEventsHandler(event))
  })
})
onUnmounted(() => {
  events.forEach((event) => {
    props.file.uploader.off(event, handlers[event])
  })
})

function fileEventsHandler(event: string) {
  handlers[event] = function (rootFile: CustomFile, file: CustomFile, message: string) {
    if (toRaw(props.file) !== toRaw(rootFile)) return
    switch (event) {
      case 'fileSuccess':
        handleFileSuccess(rootFile, file, message)
        break
      case 'fileError':
        handleFileError(rootFile, file, message)
        break
      case 'fileProgress':
        handleFileProgress()
        break
      case 'fileComplete':
        handleFileComplete(rootFile, file, message)
        break
    }
  }

  return handlers[event]
}

function actionCheck() {
  paused.value = props.file.paused
  error.value = props.file.error
  isUploading.value = props.file.isUploading()
}

function processResponse(message: string) {
  let res = message
  try {
    res = JSON.parse(message)
  } catch (e) {
    window.flags.error = !!e
  }
  response.value = res
}

function fileProgress() {
  progress.value = props.file.progress()
  averageSpeed.value = props.file.averageSpeed
  currentSpeed.value = props.file.currentSpeed
  timeRemaining.value = props.file.timeRemaining()
  uploadedSize.value = props.file.sizeUploaded()
  actionCheck()
}

function handleFileSuccess(rootFile: CustomFile, file: CustomFile, message: string) {
  processResponse(message)
}
function handleFileError(rootFile: CustomFile, file: CustomFile, message: string) {
  fileProgress()
  processResponse(message)
  error.value = true
  isComplete.value = false
  isUploading.value = false
}
function handleFileProgress() {
  fileProgress()
}
function handleFileComplete(rootFile: CustomFile, file: CustomFile, message: string) {
  if (rootFile) processResponse(message)
  fileProgress()
  error.value = false
  isComplete.value = true
  isUploading.value = false
}

const pause = () => {
  props.file.pause()
  actionCheck()
  fileProgress()
}
const resume = () => {
  props.file.resume()
  actionCheck()
}
const remove = () => {
  props.file.cancel()
}
const retry = () => {
  props.file.retry()
  actionCheck()
}

const formatedAverageSpeed = computed(() => {
  return `${Uploader.utils.formatSize(averageSpeed.value)}/s`
})

const formatedTimeRemaining = computed(() => {
  const file = props.file
  if (timeRemaining.value === Number.POSITIVE_INFINITY || timeRemaining.value === 0) {
    return ''
  }
  let parsedTimeRemaining = secondsToStr(timeRemaining.value)
  const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
  if (parseTimeRemaining) {
    parsedTimeRemaining = parseTimeRemaining(timeRemaining.value, parsedTimeRemaining)
  }
  return parsedTimeRemaining
})

function secondsToStr(temp: number) {
  const years = Math.floor(temp / 31536000)
  if (years) {
    return years + ' year' + numberEnding(years)
  }
  const days = Math.floor((temp %= 31536000) / 86400)
  if (days) {
    return days + ' day' + numberEnding(days)
  }
  const hours = Math.floor((temp %= 86400) / 3600)
  if (hours) {
    return hours + ' hour' + numberEnding(hours)
  }
  const minutes = Math.floor((temp %= 3600) / 60)
  if (minutes) {
    return minutes + ' minute' + numberEnding(minutes)
  }
  const seconds = temp % 60
  return seconds + ' second' + numberEnding(seconds)
  function numberEnding(number: number) {
    return number > 1 ? 's' : ''
  }
}
</script>

<template>
  <li class="uploader-files__item">
    <span>{{ file.name }}</span>

    <div
      v-if="isUploading"
      class="uploader-files__item__progress"
    >
      <span>{{ formatedAverageSpeed }}</span>
      <span>{{ formatedTimeRemaining }}</span>
    </div>

    <div class="uploader-files__item__controls">
      <template v-if="paused || isUploading">
        <span
          v-if="!paused"
          class="controls__pause"
          @click="pause"
        >
          <NIcon :size="18">
            <PauseRound />
          </NIcon>
        </span>
        <span
          v-else
          class="controls__resume"
          @click="resume"
        >
          <NIcon :size="18">
            <PlayArrowRound />
          </NIcon>
        </span>
        <span
          v-if="error"
          class="controls__retry"
          @click="retry"
        >
          <NIcon :size="18">
            <AutorenewRound />
          </NIcon>
        </span>
      </template>
      <span
        class="controls__remove"
        @click="remove"
      >
        <NIcon :size="18">
          <DeleteOutlineRound />
        </NIcon>
      </span>
    </div>
  </li>
</template>

<style scoped>
.uploader-files__item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0 1em;
  min-width: 150px;
}

.uploader-files__item__controls {
  display: flex;
  gap: 0 0.5em;
}

.uploader-files__item__controls > span {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.uploader-files__item__controls > span:hover {
  color: #e74c3c;
}

.uploader-files__item__progress {
  display: flex;
  gap: 0 0.5em;
  min-width: 150px;
  color: #666;
}

.uploader-files__item__progress > span:last-child {
  color: #16a085;
}
</style>
